import torch
import numpy as np

'''

张量  Tensor  



'''

# 1 生成张量
data = [[1, 2], [3, 4]]
x_data = torch.tensor(data)
print(x_data)
print(type(x_data))

# 2 numpy 转张量
np_array = np.array(data)

x_np = torch.from_numpy(np_array)
print(type(x_np))

# 3 已有张量生成新的张量

x_ones = torch.ones_like(x_data)
print(f"Ones Tensor: \n {x_ones} \n")

x_rand = torch.rand_like(x_data, dtype=torch.float)

print(f"Random Tensor: \n {x_rand} \n")

# 4. 通过指定数据维度来生成张量
shape = (2, 3,)
rand_tensor = torch.rand(shape)
ones_tensor = torch.ones(shape)
zeros_tensor = torch.zeros(shape)

print(f"Random Tensor: \n {rand_tensor} \n")
print(f"Ones Tensor: \n {ones_tensor} \n")
print(f"Zeros Tensor: \n {zeros_tensor}")

'''
张量属性
    从张量属性我们可以得到张量的维数、数据类型以及它们所存储的设备(CPU或GPU)。

'''

tensor = torch.rand(3, 4)

print(f"Shape of tensor: {tensor.shape}")
print(f"Datatype of tensor: {tensor.dtype}")  # 类型
print(f"Device tensor is stored on: {tensor.device}")  # 存储位置

'''
张量运算
有超过100种张量相关的运算操作, 例如转置、索引、切片、数学运算、线性代数、随机采样等。更多的运算

所有这些运算都可以在GPU上运行(相对于CPU来说可以达到更高的运算速度)

'''

# 判断当前环境GPU是否可用, 然后将tensor导入GPU内运行
if torch.cuda.is_available():
    print("gpu")
    tensor = tensor.to('cuda')

# 1. 张量的索引和切片
tensor = torch.ones(4, 4)
tensor[:, 1] = 0
print(tensor)

# 2. 张量的拼接

t1 = torch.cat([tensor, tensor, tensor], dim=1)
print(t1)

# 3. 张量的拼接的乘积和矩阵乘法

# 逐个元素相乘结果
print(f"tensor.mul(tensor): \n {tensor.mul(tensor)} \n")
# 等价写法:
print(f"tensor * tensor: \n {tensor * tensor}")

# 下面写法表示张量与张量的矩阵乘法:
print(f"tensor.matmul(tensor.T): \n {tensor.matmul(tensor.T)} \n")
# 等价写法:
print(f"tensor @ tensor.T: \n {tensor @ tensor.T}")

# 4. 自动赋值运算
# 自动赋值运算通常在方法后有 _ 作为后缀, 例如: x.copy_(y), x.t_()操作会改变 x 的取值。
# 注意:
#
# 自动赋值运算虽然可以节省内存, 但在求导时会因为丢失了中间过程而导致一些问题, 所以我们并不鼓励使用它。
print("======================4. 自动赋值运算==========================")
print(tensor,"\n")
tensor.add_(5)
print(tensor)
print("======================Tensor与Numpy的转化==========================")
'''
Tensor与Numpy的转化
张量和Numpy array数组在CPU上可以共用一块内存区域, 改变其中一个另一个也会随之改变。 

'''
# 1. 由张量变换为Numpy array数组

t=torch.ones(5)
print(f"t:{t}")
n=t.numpy()
print(f"n:{n}")

# 修改张量的值，则Numpy array数组值也会随之改变。
t.add_(1)
print(f"t:{t}")
print(f"n:{n}")
# 2. 由Numpy array数组转为张量
n=np.ones(5)
t=torch.from_numpy(n)
print(t)

# 修改Numpy array数组的值，则张量值也会随之改变
np.add(n,1,out=n)
print(f"t:{t}")
print(f"n:{n}")



